/**
 * Copyright 2022-9999 the original author or authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.binghe.im.platform.common.filter;


import cn.hutool.core.io.IoUtil;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;

/**
 * @author binghe(微信 : hacker_binghe)
 * @version 1.0.0
 * @description 带缓存的HttpServletRequest
 * 该类用于缓存 HttpServletRequest 中的请求体，以便后续多次读取。
 * 它通过继承 HttpServletRequestWrapper 类，重写 getInputStream 和 getReader 方法，
 * 实现对请求体的缓存功能。
 * 主要作用是为了解决 在 Java Servlet 中，HttpServletRequest 的请求体（InputStream 或 Reader）只能被读取一次的问题。
 * 当需要多次读取请求体时，通过该类可以将请求体缓存起来，后续可以多次读取。
 * @github https://github.com/binghe001
 * @copyright 公众号: 冰河技术
 */
public class CacheHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] requestBody;
    // 原始的 HttpServletRequest 对象
    private final HttpServletRequest request;

    public CacheHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        this.request = request;
    }

    /**
     * 重写 getInputStream 方法，实现请求体的缓存功能。
     * 若请求体尚未缓存，则将原始请求体的内容读取并存储到字节数组中。
     * 后续调用该方法时，直接从缓存的字节数组创建输入流，从而实现请求体的多次读取。
     * @return 一个新的 ServletInputStream 对象，其数据来源于缓存的请求体
     * @throws IOException 当读取输入流发生错误时抛出
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (null == this.requestBody) {
            // 如果请求体还未缓存，将请求体的内容读取到 ByteArrayOutputStream 中
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            IoUtil.copy(request.getInputStream(), baos);
            this.requestBody = baos.toByteArray();
        }
        ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener listener) {
            }

            @Override
            public int read() {
                return bais.read();
            }
        };
    }

    /**
     * 重写 getReader 方法，通过调用 getInputStream 方法获取输入流，
     * 并将其包装为 BufferedReader 对象，方便以字符流的方式读取缓存的请求体。
     * @return 一个新的 BufferedReader 对象，用于读取缓存的请求体
     * @throws IOException 当读取输入流发生错误时抛出
     */
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
}
